package com.ctl;

/*
 * Copyright (c) 2017.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.*;
import org.mybatis.generator.api.dom.xml.*;
import org.mybatis.generator.codegen.mybatis3.ListUtilities;
import org.mybatis.generator.codegen.mybatis3.MyBatis3FormattingUtilities;
import org.mybatis.generator.codegen.mybatis3.xmlmapper.elements.InsertElementGenerator;
import org.mybatis.generator.internal.util.StringUtility;
import org.mybatis.generator.api.dom.xml.XmlElement;

import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * <p>Title: run</p>
 * <p>Description: 批量插入插件</p>
 * <p>Copyright: Copyright (c) 2022</p>
 * <p>Company: https://gitee.com/ctllin</p>
 * 参考{@link  InsertElementGenerator}
 * @author ctl
 * @version 1.0
 * @date 2022-04-01 10:09
 */
public class MyBatchInsertPlugin extends PluginAdapter {
    public static final String METHOD_BATCH_INSERT = "batchInsert";  // 方法名
    public static final String METHOD_BATCH_INSERT_SELECTIVE = "batchInsertSelective";  // 方法名
    public static final String PRO_ALLOW_MULTI_QUERIES = "allowMultiQueries";   // property allowMultiQueries
    private boolean allowMultiQueries = false;  // 是否允许多sql提交

    @Override
    public boolean validate(List<String> warnings) {
        return true;
    }

    public void setProperties(Properties properties) {
        super.setProperties(properties);
        this.allowMultiQueries = StringUtility.isTrue(properties.getProperty("allowMultiQueries"));
    }

    /**
     *
     * @param interfaze
     * @param introspectedTable
     * @return
     */
    @Override
    public boolean clientGenerated(Interface interfaze, IntrospectedTable introspectedTable) {
        // 1. batchInsert
        FullyQualifiedJavaType listType = FullyQualifiedJavaType.getNewListInstance();
        listType.addTypeArgument(introspectedTable.getRules().calculateAllFieldsClass());
        Method batchInsertSelectiveMethod = new Method(METHOD_BATCH_INSERT_SELECTIVE);
        batchInsertSelectiveMethod.setReturnType(FullyQualifiedJavaType.getIntInstance());
        batchInsertSelectiveMethod.setVisibility(JavaVisibility.DEFAULT);
        batchInsertSelectiveMethod.addParameter(new Parameter(listType, "list", "@Param(\"list\")"));
        FullyQualifiedJavaType selectiveType = new FullyQualifiedJavaType(introspectedTable.getRules().calculateAllFieldsClass().getShortName() + "." + MyColumnPlugin.ENUM_NAME);
        batchInsertSelectiveMethod.addParameter(new Parameter(selectiveType, "selective", "@Param(\"selective\")", true));
        batchInsertSelectiveMethod.setAbstract(true);

        Method batchInsertMethod = new Method(METHOD_BATCH_INSERT);
        batchInsertMethod.setReturnType(FullyQualifiedJavaType.getIntInstance());
        batchInsertMethod.setVisibility(JavaVisibility.DEFAULT);
        batchInsertMethod.addParameter(new Parameter(listType, "list", "@Param(\"list\")"));
        batchInsertMethod.setAbstract(true);

        interfaze.addMethod(batchInsertSelectiveMethod);
        interfaze.addMethod(batchInsertMethod);
        return true;
    }

    /**
     * SQL Map Methods 生成
     * 具体执行顺序 http://www.mybatis.org/generator/reference/pluggingIn.html
     * {@link InsertElementGenerator}
     *
     * @param document
     * @param introspectedTable
     * @return
     */
    @Override
    public boolean sqlMapDocumentGenerated(Document document, IntrospectedTable introspectedTable) {
        // 1. batchInsert
        XmlElement batchInsertEle = new XmlElement("insert");
        batchInsertEle.addAttribute(new Attribute("id", METHOD_BATCH_INSERT));
        // 参数类型
        batchInsertEle.addAttribute(new Attribute("parameterType", "map"));
        batchInsertEle.addElement(new TextElement("insert into " + introspectedTable.getFullyQualifiedTableNameAtRuntime()));
        List<IntrospectedColumn> allColumns = introspectedTable.getAllColumns();
        StringBuffer buffer = new StringBuffer();
        buffer.append("(");
        for (int i = 0; i < allColumns.size(); i++) {
            IntrospectedColumn introspectedColumn = allColumns.get(i);
            if (i != allColumns.size() - 1) {
                buffer.append(introspectedColumn.getActualColumnName()).append(",");
            } else {
                buffer.append(introspectedColumn.getActualColumnName());
            }
        }
        buffer.append(")");

        batchInsertEle.addElement(new TextElement(buffer.toString()));
        StringBuilder valuesClause = new StringBuilder();
        valuesClause.append("("); //$NON-NLS-1$
        List<IntrospectedColumn> columns = ListUtilities.removeIdentityAndGeneratedAlwaysColumns(introspectedTable.getAllColumns());
        for (int i = 0; i < columns.size(); i++) {
            IntrospectedColumn introspectedColumn = columns.get(i);
            valuesClause.append(MyBatis3FormattingUtilities.getParameterClause(introspectedColumn,"item."));
            if (i + 1 < columns.size()) {
                valuesClause.append(", "); //$NON-NLS-1$
            }
        }

        valuesClause.append(')');
        // 添加foreach节点
        XmlElement foreachElement = new XmlElement("foreach");
        foreachElement.addAttribute(new Attribute("collection", "list"));
        foreachElement.addAttribute(new Attribute("item", "item"));
        foreachElement.addAttribute(new Attribute("separator", ","));
        foreachElement.addElement(new TextElement(valuesClause.toString()));
        // values 构建
        batchInsertEle.addElement(new TextElement("values"));
        batchInsertEle.addElement(foreachElement);
        document.getRootElement().addElement(batchInsertEle);

        // 2. batchInsertSelective
        XmlElement batchInsertSelectiveEle = new XmlElement("insert");
        batchInsertSelectiveEle.addAttribute(new Attribute("id", METHOD_BATCH_INSERT_SELECTIVE));
        // 参数类型
        batchInsertSelectiveEle.addAttribute(new Attribute("parameterType", "map"));
        batchInsertSelectiveEle.getElements().addAll(this.generateSelectiveEnhancedEles(introspectedTable));
        document.getRootElement().addElement(batchInsertSelectiveEle);
        return true;
    }

    /**
     * 生成insert selective 增强的插入语句
     *
     * @param introspectedTable
     * @return
     */
    private List<VisitableElement> generateSelectiveEnhancedEles(IntrospectedTable introspectedTable) {
        List<VisitableElement> eles = new ArrayList<>();
        eles.add(new TextElement("insert into " + introspectedTable.getFullyQualifiedTableNameAtRuntime() + " ("));
        XmlElement foreachInsertColumns = new XmlElement("foreach");
        foreachInsertColumns.addAttribute(new Attribute("collection", "selective"));
        foreachInsertColumns.addAttribute(new Attribute("item", "column"));
        foreachInsertColumns.addAttribute(new Attribute("separator", ","));
        foreachInsertColumns.addElement(new TextElement("${column.column}"));
        eles.add(foreachInsertColumns);
        eles.add(new TextElement(")"));
        // values
        eles.add(new TextElement("values"));
        // foreach values
        XmlElement foreachValues = new XmlElement("foreach");
        foreachValues.addAttribute(new Attribute("collection", "list"));
        foreachValues.addAttribute(new Attribute("item", "item"));
        foreachValues.addAttribute(new Attribute("separator", ","));
        foreachValues.addElement(new TextElement("("));
        // foreach 所有插入的列，比较是否存在
        XmlElement foreachInsertColumnsCheck = new XmlElement("foreach");
        foreachInsertColumnsCheck.addAttribute(new Attribute("collection", "selective"));
        foreachInsertColumnsCheck.addAttribute(new Attribute("item", "column"));
        foreachInsertColumnsCheck.addAttribute(new Attribute("separator", ","));

        // 所有表字段
        List<IntrospectedColumn> columns = ListUtilities.removeIdentityAndGeneratedAlwaysColumns(introspectedTable.getAllColumns());
        List<IntrospectedColumn> columns1 = ListUtilities.removeIdentityAndGeneratedAlwaysColumns(introspectedTable.getAllColumns());
        for (int i = 0; i < columns1.size(); i++) {
            IntrospectedColumn introspectedColumn = columns.get(i);
            XmlElement check = new XmlElement("if");
            check.addAttribute(new Attribute("test", "'" + introspectedColumn.getActualColumnName() + "'.toString() == column.column"));
            check.addElement(new TextElement(MyBatis3FormattingUtilities.getParameterClause(introspectedColumn, "item.")));
            foreachInsertColumnsCheck.addElement(check);
        }
        foreachValues.addElement(foreachInsertColumnsCheck);
        foreachValues.addElement(new TextElement(")"));
        eles.add(foreachValues);
        return eles;
    }
}